package com.yvan.dsf.router;

import com.alibaba.dubbo.common.Constants;
import com.alibaba.dubbo.common.URL;
import com.alibaba.dubbo.common.logger.Logger;
import com.alibaba.dubbo.common.logger.LoggerFactory;
import com.alibaba.dubbo.rpc.Invocation;
import com.alibaba.dubbo.rpc.Invoker;
import com.alibaba.dubbo.rpc.RpcContext;
import com.alibaba.dubbo.rpc.RpcException;
import com.alibaba.dubbo.rpc.cluster.Router;

import java.util.ArrayList;
import java.util.List;

/**
 * ParamRouter
 *
 * @author edifier.will
 */

public class ParamRouter implements Router, Comparable<Router> {

    private static final Logger logger = LoggerFactory.getLogger(ParamRouter.class);

    private static final String DEBUG_DOMAIN_NAME="debugger";

    private final URL url;
    private final int priority;

    public ParamRouter(URL url){
        this.url = url;
        this.priority = url.getParameter(Constants.PRIORITY_KEY, 0);
    }

    public <T> List<Invoker<T>> route(List<Invoker<T>> invokers, URL url, Invocation invocation)
            throws RpcException {
        if (invokers == null || invokers.size() == 0) {
            return invokers;
        }

        try {
            if(RpcContext.getContext().getAttachments().containsKey(DEBUG_DOMAIN_NAME)){
                List<Invoker<T>> result = new ArrayList<Invoker<T>>();
                for (Invoker<T> invoker : getInvokersByParamKey(invokers,DEBUG_DOMAIN_NAME,true)) {
                    if (matchParam(invoker.getUrl(), DEBUG_DOMAIN_NAME,  RpcContext.getContext().getAttachment(DEBUG_DOMAIN_NAME))) {
                        result.add(invoker);
                    }
                }
                if(result.isEmpty()){
                    return getInvokersByParamKey(invokers,DEBUG_DOMAIN_NAME,false);
                }
                return result;
            }else{
                return getInvokersByParamKey(invokers,DEBUG_DOMAIN_NAME,false);
            }
        } catch (Throwable t) {
            logger.error("Failed to execute param router rule: " + getUrl() + ", invokers: " + invokers + ", cause: " + t.getMessage(), t);
        }
        return invokers;
    }

    public URL getUrl() {
        return url;
    }

    private boolean matchParam(URL url, String paramKey,String paramValue){
        return paramValue.equals(url.getParameter(paramKey));
    }

    private boolean containParam(URL url, String paramKey){
        return url.getParameters().containsKey(paramKey);
    }

    private <T> List<Invoker<T>> getInvokersByParamKey(List<Invoker<T>> invokers,String paramKey,boolean isWithTag){
        List<Invoker<T>> result = new ArrayList<Invoker<T>>();
        for (Invoker<T> invoker : invokers) {
            if(isWithTag==containParam(invoker.getUrl(),paramKey)){
                result.add(invoker);
            }
        }
       return result;
    }

    public int compareTo(Router o) {
        if (o == null || o.getClass() != ParamRouter.class) {
            return 1;
        }
        ParamRouter c = (ParamRouter) o;
        return this.priority == c.priority ? url.toFullString().compareTo(c.url.toFullString()) : (this.priority > c.priority ? 1 : -1);
    }
}